// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// package runtime -- go2cs converted at 2022 March 13 05:26:14 UTC
// import "runtime" ==> using runtime = go.runtime_package
// Original source: C:\Program Files\Go\src\runtime\panic.go
namespace go;

using abi = @internal.abi_package;
using goexperiment = @internal.goexperiment_package;
using atomic = runtime.@internal.atomic_package;
using sys = runtime.@internal.sys_package;
using @unsafe = @unsafe_package;


// We have two different ways of doing defers. The older way involves creating a
// defer record at the time that a defer statement is executing and adding it to a
// defer chain. This chain is inspected by the deferreturn call at all function
// exits in order to run the appropriate defer calls. A cheaper way (which we call
// open-coded defers) is used for functions in which no defer statements occur in
// loops. In that case, we simply store the defer function/arg information into
// specific stack slots at the point of each defer statement, as well as setting a
// bit in a bitmask. At each function exit, we add inline code to directly make
// the appropriate defer calls based on the bitmask and fn/arg information stored
// on the stack. During panic/Goexit processing, the appropriate defer calls are
// made using extra funcdata info that indicates the exact stack slots that
// contain the bitmask and defer fn/args.

// Check to make sure we can really generate a panic. If the panic
// was generated from the runtime, or from inside malloc, then convert
// to a throw of msg.
// pc should be the program counter of the compiler-generated code that
// triggered this panic.

using System;
public static partial class runtime_package {

private static void panicCheck1(System.UIntPtr pc, @string msg) {
    if (sys.GoarchWasm == 0 && hasPrefix(funcname(findfunc(pc)), "runtime.")) { 
        // Note: wasm can't tail call, so we can't get the original caller's pc.
        throw(msg);
    }
    var gp = getg();
    if (gp != null && gp.m != null && gp.m.mallocing != 0) {
        throw(msg);
    }
}

// Same as above, but calling from the runtime is allowed.
//
// Using this function is necessary for any panic that may be
// generated by runtime.sigpanic, since those are always called by the
// runtime.
private static void panicCheck2(@string err) { 
    // panic allocates, so to avoid recursive malloc, turn panics
    // during malloc into throws.
    var gp = getg();
    if (gp != null && gp.m != null && gp.m.mallocing != 0) {
        throw(err);
    }
}

// Many of the following panic entry-points turn into throws when they
// happen in various runtime contexts. These should never happen in
// the runtime, and if they do, they indicate a serious issue and
// should not be caught by user code.
//
// The panic{Index,Slice,divide,shift} functions are called by
// code generated by the compiler for out of bounds index expressions,
// out of bounds slice expressions, division by zero, and shift by negative.
// The panicdivide (again), panicoverflow, panicfloat, and panicmem
// functions are called by the signal handler when a signal occurs
// indicating the respective problem.
//
// Since panic{Index,Slice,shift} are never called directly, and
// since the runtime package should never have an out of bounds slice
// or array reference or negative shift, if we see those functions called from the
// runtime package we turn the panic into a throw. That will dump the
// entire runtime stack for easier debugging.
//
// The entry points called by the signal handler will be called from
// runtime.sigpanic, so we can't disallow calls from the runtime to
// these (they always look like they're called from the runtime).
// Hence, for these, we just check for clearly bad runtime conditions.
//
// The panic{Index,Slice} functions are implemented in assembly and tail call
// to the goPanic{Index,Slice} functions below. This is done so we can use
// a space-minimal register calling convention.

// failures in the comparisons for s[x], 0 <= x < y (y == len(s))
private static void goPanicIndex(nint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "index out of range");
    panic(new boundsError(x:int64(x),signed:true,y:y,code:boundsIndex));
});
private static void goPanicIndexU(nuint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "index out of range");
    panic(new boundsError(x:int64(x),signed:false,y:y,code:boundsIndex));
});

// failures in the comparisons for s[:x], 0 <= x <= y (y == len(s) or cap(s))
private static void goPanicSliceAlen(nint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:true,y:y,code:boundsSliceAlen));
});
private static void goPanicSliceAlenU(nuint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:false,y:y,code:boundsSliceAlen));
});
private static void goPanicSliceAcap(nint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:true,y:y,code:boundsSliceAcap));
});
private static void goPanicSliceAcapU(nuint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:false,y:y,code:boundsSliceAcap));
});

// failures in the comparisons for s[x:y], 0 <= x <= y
private static void goPanicSliceB(nint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:true,y:y,code:boundsSliceB));
});
private static void goPanicSliceBU(nuint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:false,y:y,code:boundsSliceB));
});

// failures in the comparisons for s[::x], 0 <= x <= y (y == len(s) or cap(s))
private static void goPanicSlice3Alen(nint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:true,y:y,code:boundsSlice3Alen));
});
private static void goPanicSlice3AlenU(nuint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:false,y:y,code:boundsSlice3Alen));
});
private static void goPanicSlice3Acap(nint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:true,y:y,code:boundsSlice3Acap));
});
private static void goPanicSlice3AcapU(nuint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:false,y:y,code:boundsSlice3Acap));
});

// failures in the comparisons for s[:x:y], 0 <= x <= y
private static void goPanicSlice3B(nint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:true,y:y,code:boundsSlice3B));
});
private static void goPanicSlice3BU(nuint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:false,y:y,code:boundsSlice3B));
});

// failures in the comparisons for s[x:y:], 0 <= x <= y
private static void goPanicSlice3C(nint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:true,y:y,code:boundsSlice3C));
});
private static void goPanicSlice3CU(nuint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice bounds out of range");
    panic(new boundsError(x:int64(x),signed:false,y:y,code:boundsSlice3C));
});

// failures in the conversion (*[x]T)s, 0 <= x <= y, x == cap(s)
private static void goPanicSliceConvert(nint x, nint y) => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "slice length too short to convert to pointer to array");
    panic(new boundsError(x:int64(x),signed:true,y:y,code:boundsConvert));
});

// Implemented in assembly, as they take arguments in registers.
// Declared here to mark them as ABIInternal.
private static void panicIndex(nint x, nint y);
private static void panicIndexU(nuint x, nint y);
private static void panicSliceAlen(nint x, nint y);
private static void panicSliceAlenU(nuint x, nint y);
private static void panicSliceAcap(nint x, nint y);
private static void panicSliceAcapU(nuint x, nint y);
private static void panicSliceB(nint x, nint y);
private static void panicSliceBU(nuint x, nint y);
private static void panicSlice3Alen(nint x, nint y);
private static void panicSlice3AlenU(nuint x, nint y);
private static void panicSlice3Acap(nint x, nint y);
private static void panicSlice3AcapU(nuint x, nint y);
private static void panicSlice3B(nint x, nint y);
private static void panicSlice3BU(nuint x, nint y);
private static void panicSlice3C(nint x, nint y);
private static void panicSlice3CU(nuint x, nint y);
private static void panicSliceConvert(nint x, nint y);

private static var shiftError = error(errorString("negative shift amount"));

private static void panicshift() => func((_, panic, _) => {
    panicCheck1(getcallerpc(), "negative shift amount");
    panic(shiftError);
});

private static var divideError = error(errorString("integer divide by zero"));

private static void panicdivide() => func((_, panic, _) => {
    panicCheck2("integer divide by zero");
    panic(divideError);
});

private static var overflowError = error(errorString("integer overflow"));

private static void panicoverflow() => func((_, panic, _) => {
    panicCheck2("integer overflow");
    panic(overflowError);
});

private static var floatError = error(errorString("floating point error"));

private static void panicfloat() => func((_, panic, _) => {
    panicCheck2("floating point error");
    panic(floatError);
});

private static var memoryError = error(errorString("invalid memory address or nil pointer dereference"));

private static void panicmem() => func((_, panic, _) => {
    panicCheck2("invalid memory address or nil pointer dereference");
    panic(memoryError);
});

private static void panicmemAddr(System.UIntPtr addr) => func((_, panic, _) => {
    panicCheck2("invalid memory address or nil pointer dereference");
    panic(new errorAddressString(msg:"invalid memory address or nil pointer dereference",addr:addr));
});

// Create a new deferred function fn with siz bytes of arguments.
// The compiler turns a defer statement into a call to this.
//go:nosplit
private static void deferproc(int siz, ptr<funcval> _addr_fn) {
    ref funcval fn = ref _addr_fn.val;
 // arguments of fn follow fn
    var gp = getg();
    if (gp.m.curg != gp) {>>MARKER:FUNCTION_panicSliceConvert_BLOCK_PREFIX<< 
        // go code on the system stack can't defer
        throw("defer on system stack");
    }
    if (goexperiment.RegabiDefer && siz != 0) {>>MARKER:FUNCTION_panicSlice3CU_BLOCK_PREFIX<< 
        // TODO: Make deferproc just take a func().
        throw("defer with non-empty frame");
    }
    var sp = getcallersp();
    var argp = uintptr(@unsafe.Pointer(_addr_fn)) + @unsafe.Sizeof(fn);
    var callerpc = getcallerpc();

    var d = newdefer(siz);
    if (d._panic != null) {>>MARKER:FUNCTION_panicSlice3C_BLOCK_PREFIX<<
        throw("deferproc: d.panic != nil after newdefer");
    }
    d.link = gp._defer;
    gp._defer = d;
    d.fn = fn;
    d.pc = callerpc;
    d.sp = sp;

    if (siz == 0)     else if (siz == sys.PtrSize) 
        (uintptr.val).val;

        (deferArgs(_addr_d)) = new ptr<ptr<ptr<System.UIntPtr>>>(@unsafe.Pointer(argp));
    else 
        memmove(deferArgs(_addr_d), @unsafe.Pointer(argp), uintptr(siz));
    // deferproc returns 0 normally.
    // a deferred func that stops a panic
    // makes the deferproc return 1.
    // the code the compiler generates always
    // checks the return value and jumps to the
    // end of the function if deferproc returns != 0.
    return0(); 
    // No code can go here - the C return register has
    // been set and must not be clobbered.
}

// deferprocStack queues a new deferred function with a defer record on the stack.
// The defer record must have its siz and fn fields initialized.
// All other fields can contain junk.
// The defer record must be immediately followed in memory by
// the arguments of the defer.
// Nosplit because the arguments on the stack won't be scanned
// until the defer record is spliced into the gp._defer list.
//go:nosplit
private static void deferprocStack(ptr<_defer> _addr_d) {
    ref _defer d = ref _addr_d.val;

    var gp = getg();
    if (gp.m.curg != gp) {>>MARKER:FUNCTION_panicSlice3BU_BLOCK_PREFIX<< 
        // go code on the system stack can't defer
        throw("defer on system stack");
    }
    if (goexperiment.RegabiDefer && d.siz != 0) {>>MARKER:FUNCTION_panicSlice3B_BLOCK_PREFIX<<
        throw("defer with non-empty frame");
    }
    d.started = false;
    d.heap = false;
    d.openDefer = false;
    d.sp = getcallersp();
    d.pc = getcallerpc();
    d.framepc = 0;
    d.varp = 0 * (uintptr.val);

    (@unsafe.Pointer(_addr_d._panic)) = 0 * (uintptr.val);

    (@unsafe.Pointer(_addr_d.fd)) = 0 * (uintptr.val);

    (@unsafe.Pointer(_addr_d.link)) = uintptr(@unsafe.Pointer(gp._defer)) * (uintptr.val);

    (@unsafe.Pointer(_addr_gp._defer)) = uintptr(@unsafe.Pointer(d));

    return0(); 
    // No code can go here - the C return register has
    // been set and must not be clobbered.
}

// Small malloc size classes >= 16 are the multiples of 16: 16, 32, 48, 64, 80, 96, 112, 128, 144, ...
// Each P holds a pool for defers with small arg sizes.
// Assign defer allocations to pools by rounding to 16, to match malloc size classes.

private static readonly var deferHeaderSize = @unsafe.Sizeof(new _defer());
private static readonly var minDeferAlloc = (deferHeaderSize + 15) & ~15;
private static readonly var minDeferArgs = minDeferAlloc - deferHeaderSize;

// defer size class for arg size sz
//go:nosplit
private static System.UIntPtr deferclass(System.UIntPtr siz) {
    if (siz <= minDeferArgs) {>>MARKER:FUNCTION_panicSlice3AcapU_BLOCK_PREFIX<<
        return 0;
    }
    return (siz - minDeferArgs + 15) / 16;
}

// total size of memory block for defer with arg size sz
private static System.UIntPtr totaldefersize(System.UIntPtr siz) {
    if (siz <= minDeferArgs) {>>MARKER:FUNCTION_panicSlice3Acap_BLOCK_PREFIX<<
        return minDeferAlloc;
    }
    return deferHeaderSize + siz;
}

// Ensure that defer arg sizes that map to the same defer size class
// also map to the same malloc size class.
private static void testdefersizes() {
    array<int> m = new array<int>(len(new p().deferpool));

    {
        var i__prev1 = i;

        foreach (var (__i) in m) {
            i = __i;
            m[i] = -1;
        }
        i = i__prev1;
    }

    {
        var i__prev1 = i;

        for (var i = uintptr(0); ; i++) {>>MARKER:FUNCTION_panicSlice3AlenU_BLOCK_PREFIX<<
            var defersc = deferclass(i);
            if (defersc >= uintptr(len(m))) {>>MARKER:FUNCTION_panicSlice3Alen_BLOCK_PREFIX<<
                break;
            }
            var siz = roundupsize(totaldefersize(i));
            if (m[defersc] < 0) {>>MARKER:FUNCTION_panicSliceBU_BLOCK_PREFIX<<
                m[defersc] = int32(siz);
                continue;
            }
            if (m[defersc] != int32(siz)) {>>MARKER:FUNCTION_panicSliceB_BLOCK_PREFIX<<
                print("bad defer size class: i=", i, " siz=", siz, " defersc=", defersc, "\n");
                throw("bad defer size class");
            }
        }

        i = i__prev1;
    }
}

// The arguments associated with a deferred call are stored
// immediately after the _defer header in memory.
//go:nosplit
private static unsafe.Pointer deferArgs(ptr<_defer> _addr_d) {
    ref _defer d = ref _addr_d.val;

    if (d.siz == 0) {>>MARKER:FUNCTION_panicSliceAcapU_BLOCK_PREFIX<< 
        // Avoid pointer past the defer allocation.
        return null;
    }
    return add(@unsafe.Pointer(d), @unsafe.Sizeof(d));
}

// deferFunc returns d's deferred function. This is temporary while we
// support both modes of GOEXPERIMENT=regabidefer. Once we commit to
// that experiment, we should change the type of d.fn.
//go:nosplit
private static Action deferFunc(ptr<_defer> _addr_d) {
    ref _defer d = ref _addr_d.val;

    if (!goexperiment.RegabiDefer) {>>MARKER:FUNCTION_panicSliceAcap_BLOCK_PREFIX<<
        throw("requires GOEXPERIMENT=regabidefer");
    }
    ref Action fn = ref heap(out ptr<Action> _addr_fn);
    (funcval.val).val;

    (@unsafe.Pointer(_addr_fn)) = d.fn;
    return fn;
}

private static ptr<_type> deferType; // type of _defer struct

private static void init() {
    ref var x = ref heap(out ptr<var> _addr_x);
    x = (_defer.val)(null);
    deferType = (new ptr<ptr<ptr<ptr<ptrtype>>>>(@unsafe.Pointer(_addr_x))).elem;
}

// Allocate a Defer, usually using per-P pool.
// Each defer must be released with freedefer.  The defer is not
// added to any defer chain yet.
//
// This must not grow the stack because there may be a frame without
// stack map information when this is called.
//
//go:nosplit
private static ptr<_defer> newdefer(int siz) {
    ptr<_defer> d;
    var sc = deferclass(uintptr(siz));
    var gp = getg();
    if (sc < uintptr(len(new p().deferpool))) {>>MARKER:FUNCTION_panicSliceAlenU_BLOCK_PREFIX<<
        var pp = gp.m.p.ptr();
        if (len(pp.deferpool[sc]) == 0 && sched.deferpool[sc] != null) {>>MARKER:FUNCTION_panicSliceAlen_BLOCK_PREFIX<< 
            // Take the slow path on the system stack so
            // we don't grow newdefer's stack.
            systemstack(() => {>>MARKER:FUNCTION_panicIndexU_BLOCK_PREFIX<<
                lock(_addr_sched.deferlock);
                while (len(pp.deferpool[sc]) < cap(pp.deferpool[sc]) / 2 && sched.deferpool[sc] != null) {>>MARKER:FUNCTION_panicIndex_BLOCK_PREFIX<<
                    d = sched.deferpool[sc];
                    sched.deferpool[sc] = d.link;
                    d.link = null;
                    pp.deferpool[sc] = append(pp.deferpool[sc], d);
                }

                unlock(_addr_sched.deferlock);
            });
        }
        {
            var n = len(pp.deferpool[sc]);

            if (n > 0) {
                d = pp.deferpool[sc][n - 1];
                pp.deferpool[sc][n - 1] = null;
                pp.deferpool[sc] = pp.deferpool[sc][..(int)n - 1];
            }

        }
    }
    if (d == null) { 
        // Allocate new defer+args.
        systemstack(() => {
            var total = roundupsize(totaldefersize(uintptr(siz)));
            d = (_defer.val)(mallocgc(total, deferType, true));
        });
    }
    d.siz = siz;
    d.heap = true;
    return _addr_d!;
}

// Free the given defer.
// The defer cannot be used after this call.
//
// This must not grow the stack because there may be a frame without a
// stack map when this is called.
//
//go:nosplit
private static void freedefer(ptr<_defer> _addr_d) {
    ref _defer d = ref _addr_d.val;

    if (d._panic != null) {
        freedeferpanic();
    }
    if (d.fn != null) {
        freedeferfn();
    }
    if (!d.heap) {
        return ;
    }
    var sc = deferclass(uintptr(d.siz));
    if (sc >= uintptr(len(new p().deferpool))) {
        return ;
    }
    var pp = getg().m.p.ptr();
    if (len(pp.deferpool[sc]) == cap(pp.deferpool[sc])) { 
        // Transfer half of local cache to the central cache.
        //
        // Take this slow path on the system stack so
        // we don't grow freedefer's stack.
        systemstack(() => {
            ptr<_defer> first;            ptr<_defer> last;

            while (len(pp.deferpool[sc]) > cap(pp.deferpool[sc]) / 2) {
                var n = len(pp.deferpool[sc]);
                var d = pp.deferpool[sc][n - 1];
                pp.deferpool[sc][n - 1] = null;
                pp.deferpool[sc] = pp.deferpool[sc][..(int)n - 1];
                if (first == null) {
                    first = d;
                }
                else
 {
                    last.link = d;
                }
                last = d;
            }

            lock(_addr_sched.deferlock);
            last.link = sched.deferpool[sc];
            sched.deferpool[sc] = first;
            unlock(_addr_sched.deferlock);
        });
    }
    d.siz = 0;
    d.started = false;
    d.openDefer = false;
    d.sp = 0;
    d.pc = 0;
    d.framepc = 0;
    d.varp = 0;
    d.fd = null; 
    // d._panic and d.fn must be nil already.
    // If not, we would have called freedeferpanic or freedeferfn above,
    // both of which throw.
    d.link = null;

    pp.deferpool[sc] = append(pp.deferpool[sc], d);
}

// Separate function so that it can split stack.
// Windows otherwise runs out of stack space.
private static void freedeferpanic() { 
    // _panic must be cleared before d is unlinked from gp.
    throw("freedefer with d._panic != nil");
}

private static void freedeferfn() { 
    // fn must be cleared before d is unlinked from gp.
    throw("freedefer with d.fn != nil");
}

// Run a deferred function if there is one.
// The compiler inserts a call to this at the end of any
// function which calls defer.
// If there is a deferred function, this will call runtime·jmpdefer,
// which will jump to the deferred function such that it appears
// to have been called by the caller of deferreturn at the point
// just before deferreturn was called. The effect is that deferreturn
// is called again and again until there are no more deferred functions.
//
// Declared as nosplit, because the function should not be preempted once we start
// modifying the caller's frame in order to reuse the frame to call the deferred
// function.
//
//go:nosplit
private static void deferreturn() {
    var gp = getg();
    var d = gp._defer;
    if (d == null) {
        return ;
    }
    var sp = getcallersp();
    if (d.sp != sp) {
        return ;
    }
    if (d.openDefer) {
        var done = runOpenDeferFrame(_addr_gp, _addr_d);
        if (!done) {
            throw("unfinished open-coded defers in deferreturn");
        }
        gp._defer = d.link;
        freedefer(_addr_d);
        return ;
    }
    var argp = getcallersp() + sys.MinFrameSize;

    if (d.siz == 0)     else if (d.siz == sys.PtrSize) 
        (uintptr.val).val;

        (@unsafe.Pointer(argp)) = new ptr<ptr<ptr<System.UIntPtr>>>(deferArgs(_addr_d));
    else 
        memmove(@unsafe.Pointer(argp), deferArgs(_addr_d), uintptr(d.siz));
        var fn = d.fn;
    d.fn = null;
    gp._defer = d.link;
    freedefer(_addr_d); 
    // If the defer function pointer is nil, force the seg fault to happen
    // here rather than in jmpdefer. gentraceback() throws an error if it is
    // called with a callback on an LR architecture and jmpdefer is on the
    // stack, because the stack trace can be incorrect in that case - see
    // issue #8153).
    _ = fn.fn;
    jmpdefer(fn, argp);
}

// Goexit terminates the goroutine that calls it. No other goroutine is affected.
// Goexit runs all deferred calls before terminating the goroutine. Because Goexit
// is not a panic, any recover calls in those deferred functions will return nil.
//
// Calling Goexit from the main goroutine terminates that goroutine
// without func main returning. Since func main has not returned,
// the program continues execution of other goroutines.
// If all other goroutines exit, the program crashes.
public static void Goexit() { 
    // Run all deferred functions for the current goroutine.
    // This code is similar to gopanic, see that implementation
    // for detailed comments.
    var gp = getg(); 

    // Create a panic object for Goexit, so we can recognize when it might be
    // bypassed by a recover().
    ref _panic p = ref heap(out ptr<_panic> _addr_p);
    p.goexit = true;
    p.link = gp._panic;
    gp._panic = (_panic.val)(noescape(@unsafe.Pointer(_addr_p)));

    addOneOpenDeferFrame(_addr_gp, getcallerpc(), @unsafe.Pointer(getcallersp()));
    while (true) {
        var d = gp._defer;
        if (d == null) {
            break;
        }
        if (d.started) {
            if (d._panic != null) {
                d._panic.aborted = true;
                d._panic = null;
            }
            if (!d.openDefer) {
                d.fn = null;
                gp._defer = d.link;
                freedefer(_addr_d);
                continue;
            }
        }
        d.started = true;
        d._panic = (_panic.val)(noescape(@unsafe.Pointer(_addr_p)));
        if (d.openDefer) {
            var done = runOpenDeferFrame(_addr_gp, _addr_d);
            if (!done) { 
                // We should always run all defers in the frame,
                // since there is no panic associated with this
                // defer that can be recovered.
                throw("unfinished open-coded defers in Goexit");
            }
            if (p.aborted) { 
                // Since our current defer caused a panic and may
                // have been already freed, just restart scanning
                // for open-coded defers from this frame again.
                addOneOpenDeferFrame(_addr_gp, getcallerpc(), @unsafe.Pointer(getcallersp()));
            }
            else
 {
                addOneOpenDeferFrame(_addr_gp, 0, null);
            }
        }
        else
 {
            if (goexperiment.RegabiDefer) { 
                // Save the pc/sp in deferCallSave(), so we can "recover" back to this
                // loop if necessary.
                deferCallSave(_addr_p, deferFunc(_addr_d));
            }
            else
 { 
                // Save the pc/sp in reflectcallSave(), so we can "recover" back to this
                // loop if necessary.
                reflectcallSave(_addr_p, @unsafe.Pointer(d.fn), deferArgs(_addr_d), uint32(d.siz));
            }
        }
        if (p.aborted) { 
            // We had a recursive panic in the defer d we started, and
            // then did a recover in a defer that was further down the
            // defer chain than d. In the case of an outstanding Goexit,
            // we force the recover to return back to this loop. d will
            // have already been freed if completed, so just continue
            // immediately to the next defer on the chain.
            p.aborted = false;
            continue;
        }
        if (gp._defer != d) {
            throw("bad defer entry in Goexit");
        }
        d._panic = null;
        d.fn = null;
        gp._defer = d.link;
        freedefer(_addr_d); 
        // Note: we ignore recovers here because Goexit isn't a panic
    }
    goexit1();
}

// Call all Error and String methods before freezing the world.
// Used when crashing with panicking.
private static void preprintpanics(ptr<_panic> _addr_p) => func((defer, _, _) => {
    ref _panic p = ref _addr_p.val;

    defer(() => {
        if (recover() != null) {
            throw("panic while printing panic value");
        }
    }());
    while (p != null) {
        switch (p.arg.type()) {
            case error v:
                p.arg = v.Error();
                break;
            case stringer v:
                p.arg = v.String();
                break;
        }
        p = p.link;
    }
});

// Print all currently active panics. Used when crashing.
// Should only be called after preprintpanics.
private static void printpanics(ptr<_panic> _addr_p) {
    ref _panic p = ref _addr_p.val;

    if (p.link != null) {
        printpanics(_addr_p.link);
        if (!p.link.goexit) {
            print("\t");
        }
    }
    if (p.goexit) {
        return ;
    }
    print("panic: ");
    printany(p.arg);
    if (p.recovered) {
        print(" [recovered]");
    }
    print("\n");
}

// addOneOpenDeferFrame scans the stack for the first frame (if any) with
// open-coded defers and if it finds one, adds a single record to the defer chain
// for that frame. If sp is non-nil, it starts the stack scan from the frame
// specified by sp. If sp is nil, it uses the sp from the current defer record
// (which has just been finished). Hence, it continues the stack scan from the
// frame of the defer that just finished. It skips any frame that already has an
// open-coded _defer record, which would have been created from a previous
// (unrecovered) panic.
//
// Note: All entries of the defer chain (including this new open-coded entry) have
// their pointers (including sp) adjusted properly if the stack moves while
// running deferred functions. Also, it is safe to pass in the sp arg (which is
// the direct result of calling getcallersp()), because all pointer variables
// (including arguments) are adjusted as needed during stack copies.
private static void addOneOpenDeferFrame(ptr<g> _addr_gp, System.UIntPtr pc, unsafe.Pointer sp) {
    ref g gp = ref _addr_gp.val;

    ptr<_defer> prevDefer;
    if (sp == null) {
        prevDefer = gp._defer;
        pc = prevDefer.framepc;
        sp = @unsafe.Pointer(prevDefer.sp);
    }
    systemstack(() => {
        gentraceback(pc, uintptr(sp), 0, gp, 0, null, 0x7fffffff, (frame, unused) => {
            if (prevDefer != null && prevDefer.sp == frame.sp) { 
                // Skip the frame for the previous defer that
                // we just finished (and was used to set
                // where we restarted the stack scan)
                return true;
            }
            var f = frame.fn;
            var fd = funcdata(f, _FUNCDATA_OpenCodedDeferInfo);
            if (fd == null) {
                return true;
            } 
            // Insert the open defer record in the
            // chain, in order sorted by sp.
            var d = gp._defer;
            ptr<_defer> prev;
            while (d != null) {
                var dsp = d.sp;
                if (frame.sp < dsp) {
                    break;
                }
                if (frame.sp == dsp) {
                    if (!d.openDefer) {
                        throw("duplicated defer entry");
                    }
                    return true;
                }
                prev = d;
                d = d.link;
            }

            if (frame.fn.deferreturn == 0) {
                throw("missing deferreturn");
            }
            var (maxargsize, _) = readvarintUnsafe(fd);
            var d1 = newdefer(int32(maxargsize));
            d1.openDefer = true;
            d1._panic = null; 
            // These are the pc/sp to set after we've
            // run a defer in this frame that did a
            // recover. We return to a special
            // deferreturn that runs any remaining
            // defers and then returns from the
            // function.
            d1.pc = frame.fn.entry + uintptr(frame.fn.deferreturn);
            d1.varp = frame.varp;
            d1.fd = fd; 
            // Save the SP/PC associated with current frame,
            // so we can continue stack trace later if needed.
            d1.framepc = frame.pc;
            d1.sp = frame.sp;
            d1.link = d;
            if (prev == null) {
                gp._defer = d1;
            }
            else
 {
                prev.link = d1;
            } 
            // Stop stack scanning after adding one open defer record
            return false;
        }, null, 0);
    });
}

// readvarintUnsafe reads the uint32 in varint format starting at fd, and returns the
// uint32 and a pointer to the byte following the varint.
//
// There is a similar function runtime.readvarint, which takes a slice of bytes,
// rather than an unsafe pointer. These functions are duplicated, because one of
// the two use cases for the functions would get slower if the functions were
// combined.
private static (uint, unsafe.Pointer) readvarintUnsafe(unsafe.Pointer fd) => func((_, panic, _) => {
    uint _p0 = default;
    unsafe.Pointer _p0 = default;

    uint r = default;
    nint shift = default;
    while (true) {
        ptr<ptr<byte>> b = new ptr<ptr<ptr<byte>>>((@unsafe.Pointer(fd)));
        fd = add(fd, @unsafe.Sizeof(b));
        if (b < 128) {
            return (r + uint32(b) << (int)(shift), fd);
        }
        r += ((uint32(b) & ~128) << (int)(shift));
        shift += 7;
        if (shift > 28) {
            panic("Bad varint");
        }
    }
});

// runOpenDeferFrame runs the active open-coded defers in the frame specified by
// d. It normally processes all active defers in the frame, but stops immediately
// if a defer does a successful recover. It returns true if there are no
// remaining defers to run in the frame.
private static bool runOpenDeferFrame(ptr<g> _addr_gp, ptr<_defer> _addr_d) {
    ref g gp = ref _addr_gp.val;
    ref _defer d = ref _addr_d.val;

    var done = true;
    var fd = d.fd; 

    // Skip the maxargsize
    _, fd = readvarintUnsafe(fd);
    var (deferBitsOffset, fd) = readvarintUnsafe(fd);
    var (nDefers, fd) = readvarintUnsafe(fd);
    ptr<ptr<byte>> deferBits = new ptr<ptr<ptr<byte>>>(@unsafe.Pointer(d.varp - uintptr(deferBitsOffset)));

    for (var i = int(nDefers) - 1; i >= 0; i--) { 
        // read the funcdata info for this defer
        uint argWidth = default;        uint closureOffset = default;        uint nArgs = default;

        argWidth, fd = readvarintUnsafe(fd);
        closureOffset, fd = readvarintUnsafe(fd);
        nArgs, fd = readvarintUnsafe(fd);
        if (goexperiment.RegabiDefer && argWidth != 0) {
            throw("defer with non-empty frame");
        }
        if (deferBits & (1 << (int)(i)) == 0) {
            {
                var j__prev2 = j;

                for (var j = uint32(0); j < nArgs; j++) {
                    _, fd = readvarintUnsafe(fd);
                    _, fd = readvarintUnsafe(fd);
                    _, fd = readvarintUnsafe(fd);
                }


                j = j__prev2;
            }
            continue;
        }
        ptr<ptr<ptr<funcval>>> closure = new ptr<ptr<ptr<ptr<funcval>>>>(@unsafe.Pointer(d.varp - uintptr(closureOffset)));
        d.fn = closure;
        var deferArgs = deferArgs(_addr_d); 
        // If there is an interface receiver or method receiver, it is
        // described/included as the first arg.
        {
            var j__prev2 = j;

            for (j = uint32(0); j < nArgs; j++) {
                uint argOffset = default;                uint argLen = default;                uint argCallOffset = default;

                argOffset, fd = readvarintUnsafe(fd);
                argLen, fd = readvarintUnsafe(fd);
                argCallOffset, fd = readvarintUnsafe(fd);
                memmove(@unsafe.Pointer(uintptr(deferArgs) + uintptr(argCallOffset)), @unsafe.Pointer(d.varp - uintptr(argOffset)), uintptr(argLen));
            }


            j = j__prev2;
        }
        deferBits = deferBits & ~(1 << (int)(i)) * (uint8.val);

        (@unsafe.Pointer(d.varp - uintptr(deferBitsOffset))) = deferBits;
        var p = d._panic;
        if (goexperiment.RegabiDefer) {
            deferCallSave(_addr_p, deferFunc(_addr_d));
        }
        else
 {
            reflectcallSave(_addr_p, @unsafe.Pointer(closure), deferArgs, argWidth);
        }
        if (p != null && p.aborted) {
            break;
        }
        d.fn = null; 
        // These args are just a copy, so can be cleared immediately
        memclrNoHeapPointers(deferArgs, uintptr(argWidth));
        if (d._panic != null && d._panic.recovered) {
            done = deferBits == 0;
            break;
        }
    }

    return done;
}

// reflectcallSave calls reflectcall after saving the caller's pc and sp in the
// panic record. This allows the runtime to return to the Goexit defer processing
// loop, in the unusual case where the Goexit may be bypassed by a successful
// recover.
//
// This is marked as a wrapper by the compiler so it doesn't appear in
// tracebacks.
private static void reflectcallSave(ptr<_panic> _addr_p, unsafe.Pointer fn, unsafe.Pointer arg, uint argsize) {
    ref _panic p = ref _addr_p.val;

    if (goexperiment.RegabiDefer) {
        throw("not allowed with GOEXPERIMENT=regabidefer");
    }
    if (p != null) {
        p.argp = @unsafe.Pointer(getargp());
        p.pc = getcallerpc();
        p.sp = @unsafe.Pointer(getcallersp());
    }
    ref abi.RegArgs regs = ref heap(out ptr<abi.RegArgs> _addr_regs);
    reflectcall(null, fn, arg, argsize, argsize, argsize, _addr_regs);
    if (p != null) {
        p.pc = 0;
        p.sp = @unsafe.Pointer(null);
    }
}

// deferCallSave calls fn() after saving the caller's pc and sp in the
// panic record. This allows the runtime to return to the Goexit defer
// processing loop, in the unusual case where the Goexit may be
// bypassed by a successful recover.
//
// This is marked as a wrapper by the compiler so it doesn't appear in
// tracebacks.
private static void deferCallSave(ptr<_panic> _addr_p, Action fn) {
    ref _panic p = ref _addr_p.val;

    if (!goexperiment.RegabiDefer) {
        throw("only allowed with GOEXPERIMENT=regabidefer");
    }
    if (p != null) {
        p.argp = @unsafe.Pointer(getargp());
        p.pc = getcallerpc();
        p.sp = @unsafe.Pointer(getcallersp());
    }
    fn();
    if (p != null) {
        p.pc = 0;
        p.sp = @unsafe.Pointer(null);
    }
}

// The implementation of the predeclared function panic.
private static void gopanic(object e) {
    var gp = getg();
    if (gp.m.curg != gp) {
        print("panic: ");
        printany(e);
        print("\n");
        throw("panic on system stack");
    }
    if (gp.m.mallocing != 0) {
        print("panic: ");
        printany(e);
        print("\n");
        throw("panic during malloc");
    }
    if (gp.m.preemptoff != "") {
        print("panic: ");
        printany(e);
        print("\n");
        print("preempt off reason: ");
        print(gp.m.preemptoff);
        print("\n");
        throw("panic during preemptoff");
    }
    if (gp.m.locks != 0) {
        print("panic: ");
        printany(e);
        print("\n");
        throw("panic holding locks");
    }
    ref _panic p = ref heap(out ptr<_panic> _addr_p);
    p.arg = e;
    p.link = gp._panic;
    gp._panic = (_panic.val)(noescape(@unsafe.Pointer(_addr_p)));

    atomic.Xadd(_addr_runningPanicDefers, 1); 

    // By calculating getcallerpc/getcallersp here, we avoid scanning the
    // gopanic frame (stack scanning is slow...)
    addOneOpenDeferFrame(_addr_gp, getcallerpc(), @unsafe.Pointer(getcallersp()));

    while (true) {
        var d = gp._defer;
        if (d == null) {
            break;
        }
        if (d.started) {
            if (d._panic != null) {
                d._panic.aborted = true;
            }
            d._panic = null;
            if (!d.openDefer) { 
                // For open-coded defers, we need to process the
                // defer again, in case there are any other defers
                // to call in the frame (not including the defer
                // call that caused the panic).
                d.fn = null;
                gp._defer = d.link;
                freedefer(_addr_d);
                continue;
            }
        }
        d.started = true; 

        // Record the panic that is running the defer.
        // If there is a new panic during the deferred call, that panic
        // will find d in the list and will mark d._panic (this panic) aborted.
        d._panic = (_panic.val)(noescape(@unsafe.Pointer(_addr_p)));

        var done = true;
        if (d.openDefer) {
            done = runOpenDeferFrame(_addr_gp, _addr_d);
            if (done && !d._panic.recovered) {
                addOneOpenDeferFrame(_addr_gp, 0, null);
            }
        }
        else
 {
            p.argp = @unsafe.Pointer(getargp());

            if (goexperiment.RegabiDefer) {
                var fn = deferFunc(_addr_d);
                fn();
            }
            else
 { 
                // Pass a dummy RegArgs since we'll only take this path if
                // we're not using the register ABI.
                ref abi.RegArgs regs = ref heap(out ptr<abi.RegArgs> _addr_regs);
                reflectcall(null, @unsafe.Pointer(d.fn), deferArgs(_addr_d), uint32(d.siz), uint32(d.siz), uint32(d.siz), _addr_regs);
            }
        }
        p.argp = null; 

        // Deferred function did not panic. Remove d.
        if (gp._defer != d) {
            throw("bad defer entry in panic");
        }
        d._panic = null; 

        // trigger shrinkage to test stack copy. See stack_test.go:TestStackPanic
        //GC()

        var pc = d.pc;
        var sp = @unsafe.Pointer(d.sp); // must be pointer so it gets adjusted during stack copy
        if (done) {
            d.fn = null;
            gp._defer = d.link;
            freedefer(_addr_d);
        }
        if (p.recovered) {
            gp._panic = p.link;
            if (gp._panic != null && gp._panic.goexit && gp._panic.aborted) { 
                // A normal recover would bypass/abort the Goexit.  Instead,
                // we return to the processing loop of the Goexit.
                gp.sigcode0 = uintptr(gp._panic.sp);
                gp.sigcode1 = uintptr(gp._panic.pc);
                mcall(recovery);
                throw("bypassed recovery failed"); // mcall should not return
            }
            atomic.Xadd(_addr_runningPanicDefers, -1); 

            // Remove any remaining non-started, open-coded
            // defer entries after a recover, since the
            // corresponding defers will be executed normally
            // (inline). Any such entry will become stale once
            // we run the corresponding defers inline and exit
            // the associated stack frame.
            d = gp._defer;
            ptr<_defer> prev;
            if (!done) { 
                // Skip our current frame, if not done. It is
                // needed to complete any remaining defers in
                // deferreturn()
                prev = d;
                d = d.link;
            }
            while (d != null) {
                if (d.started) { 
                    // This defer is started but we
                    // are in the middle of a
                    // defer-panic-recover inside of
                    // it, so don't remove it or any
                    // further defer entries
                    break;
                }
                if (d.openDefer) {
                    if (prev == null) {
                        gp._defer = d.link;
                    }
                    else
 {
                        prev.link = d.link;
                    }
                    var newd = d.link;
                    freedefer(_addr_d);
                    d = newd;
                }
                else
 {
                    prev = d;
                    d = d.link;
                }
            }


            gp._panic = p.link; 
            // Aborted panics are marked but remain on the g.panic list.
            // Remove them from the list.
            while (gp._panic != null && gp._panic.aborted) {
                gp._panic = gp._panic.link;
            }

            if (gp._panic == null) { // must be done with signal
                gp.sig = 0;
            } 
            // Pass information about recovering frame to recovery.
            gp.sigcode0 = uintptr(sp);
            gp.sigcode1 = pc;
            mcall(recovery);
            throw("recovery failed"); // mcall should not return
        }
    } 

    // ran out of deferred calls - old-school panic now
    // Because it is unsafe to call arbitrary user code after freezing
    // the world, we call preprintpanics to invoke all necessary Error
    // and String methods to prepare the panic strings before startpanic.
    preprintpanics(_addr_gp._panic);

    fatalpanic(_addr_gp._panic) * (int.val);

    (null) = 0; // not reached
}

// getargp returns the location where the caller
// writes outgoing function call arguments.
//go:nosplit
//go:noinline
private static System.UIntPtr getargp() {
    return getcallersp() + sys.MinFrameSize;
}

// The implementation of the predeclared function recover.
// Cannot split the stack because it needs to reliably
// find the stack segment of its caller.
//
// TODO(rsc): Once we commit to CopyStackAlways,
// this doesn't need to be nosplit.
//go:nosplit
private static void gorecover(System.UIntPtr argp) { 
    // Must be in a function running as part of a deferred call during the panic.
    // Must be called from the topmost function of the call
    // (the function used in the defer statement).
    // p.argp is the argument pointer of that topmost deferred function call.
    // Compare against argp reported by caller.
    // If they match, the caller is the one who can recover.
    var gp = getg();
    var p = gp._panic;
    if (p != null && !p.goexit && !p.recovered && argp == uintptr(p.argp)) {
        p.recovered = true;
        return p.arg;
    }
    return null;
}

//go:linkname sync_throw sync.throw
private static void sync_throw(@string s) {
    throw(s);
}

//go:nosplit
private static void @throw(@string s) { 
    // Everything throw does should be recursively nosplit so it
    // can be called even when it's unsafe to grow the stack.
    systemstack(() => {
        print("fatal error: ", s, "\n");
    });
    var gp = getg();
    if (gp.m.throwing == 0) {
        gp.m.throwing = 1;
    }
    fatalthrow() * (int.val);

    (null) = 0; // not reached
}

// runningPanicDefers is non-zero while running deferred functions for panic.
// runningPanicDefers is incremented and decremented atomically.
// This is used to try hard to get a panic stack trace out when exiting.
private static uint runningPanicDefers = default;

// panicking is non-zero when crashing the program for an unrecovered panic.
// panicking is incremented and decremented atomically.
private static uint panicking = default;

// paniclk is held while printing the panic information and stack trace,
// so that two concurrent panics don't overlap their output.
private static mutex paniclk = default;

// Unwind the stack after a deferred function calls recover
// after a panic. Then arrange to continue running as though
// the caller of the deferred function returned normally.
private static void recovery(ptr<g> _addr_gp) {
    ref g gp = ref _addr_gp.val;
 
    // Info about defer passed in G struct.
    var sp = gp.sigcode0;
    var pc = gp.sigcode1; 

    // d's arguments need to be in the stack.
    if (sp != 0 && (sp < gp.stack.lo || gp.stack.hi < sp)) {
        print("recover: ", hex(sp), " not in [", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n");
        throw("bad recovery");
    }
    gp.sched.sp = sp;
    gp.sched.pc = pc;
    gp.sched.lr = 0;
    gp.sched.ret = 1;
    gogo(_addr_gp.sched);
}

// fatalthrow implements an unrecoverable runtime throw. It freezes the
// system, prints stack traces starting from its caller, and terminates the
// process.
//
//go:nosplit
private static void fatalthrow() {
    var pc = getcallerpc();
    var sp = getcallersp();
    var gp = getg(); 
    // Switch to the system stack to avoid any stack growth, which
    // may make things worse if the runtime is in a bad state.
    systemstack(() => {
        startpanic_m();

        if (dopanic_m(_addr_gp, pc, sp)) { 
            // crash uses a decent amount of nosplit stack and we're already
            // low on stack in throw, so crash on the system stack (unlike
            // fatalpanic).
            crash();
        }
        exit(2);
    }) * (int.val);

    (null) = 0; // not reached
}

// fatalpanic implements an unrecoverable panic. It is like fatalthrow, except
// that if msgs != nil, fatalpanic also prints panic messages and decrements
// runningPanicDefers once main is blocked from exiting.
//
//go:nosplit
private static void fatalpanic(ptr<_panic> _addr_msgs) {
    ref _panic msgs = ref _addr_msgs.val;

    var pc = getcallerpc();
    var sp = getcallersp();
    var gp = getg();
    bool docrash = default; 
    // Switch to the system stack to avoid any stack growth, which
    // may make things worse if the runtime is in a bad state.
    systemstack(() => {
        if (startpanic_m() && msgs != null) { 
            // There were panic messages and startpanic_m
            // says it's okay to try to print them.

            // startpanic_m set panicking, which will
            // block main from exiting, so now OK to
            // decrement runningPanicDefers.
            atomic.Xadd(_addr_runningPanicDefers, -1);

            printpanics(_addr_msgs);
        }
        docrash = dopanic_m(_addr_gp, pc, sp);
    });

    if (docrash) { 
        // By crashing outside the above systemstack call, debuggers
        // will not be confused when generating a backtrace.
        // Function crash is marked nosplit to avoid stack growth.
        crash();
    }
    systemstack(() => {
        exit(2);
    }) * (int.val);

    (null) = 0; // not reached
}

// startpanic_m prepares for an unrecoverable panic.
//
// It returns true if panic messages should be printed, or false if
// the runtime is in bad shape and should just print stacks.
//
// It must not have write barriers even though the write barrier
// explicitly ignores writes once dying > 0. Write barriers still
// assume that g.m.p != nil, and this function may not have P
// in some contexts (e.g. a panic in a signal handler for a signal
// sent to an M with no P).
//
//go:nowritebarrierrec
private static bool startpanic_m() {
    var _g_ = getg();
    if (mheap_.cachealloc.size == 0) { // very early
        print("runtime: panic before malloc heap initialized\n");
    }
    _g_.m.mallocing++; 

    // If we're dying because of a bad lock count, set it to a
    // good lock count so we don't recursively panic below.
    if (_g_.m.locks < 0) {
        _g_.m.locks = 1;
    }

    if (_g_.m.dying == 0) 
    {
        // Setting dying >0 has the side-effect of disabling this G's writebuf.
        _g_.m.dying = 1;
        atomic.Xadd(_addr_panicking, 1);
        lock(_addr_paniclk);
        if (debug.schedtrace > 0 || debug.scheddetail > 0) {
            schedtrace(true);
        }
        freezetheworld();
        return true;
        goto __switch_break0;
    }
    if (_g_.m.dying == 1) 
    {
        // Something failed while panicking.
        // Just print a stack trace and exit.
        _g_.m.dying = 2;
        print("panic during panic\n");
        return false;
        goto __switch_break0;
    }
    if (_g_.m.dying == 2) 
    {
        // This is a genuine bug in the runtime, we couldn't even
        // print the stack trace successfully.
        _g_.m.dying = 3;
        print("stack trace unavailable\n");
        exit(4);
    }
    // default: 
        // Can't even print! Just exit.
        exit(5);
        return false; // Need to return something.

    __switch_break0:;
}

private static bool didothers = default;
private static mutex deadlock = default;

private static bool dopanic_m(ptr<g> _addr_gp, System.UIntPtr pc, System.UIntPtr sp) {
    ref g gp = ref _addr_gp.val;

    if (gp.sig != 0) {
        var signame = signame(gp.sig);
        if (signame != "") {
            print("[signal ", signame);
        }
        else
 {
            print("[signal ", hex(gp.sig));
        }
        print(" code=", hex(gp.sigcode0), " addr=", hex(gp.sigcode1), " pc=", hex(gp.sigpc), "]\n");
    }
    var (level, all, docrash) = gotraceback();
    var _g_ = getg();
    if (level > 0) {
        if (gp != gp.m.curg) {
            all = true;
        }
        if (gp != gp.m.g0) {
            print("\n");
            goroutineheader(gp);
            traceback(pc, sp, 0, gp);
        }
        else if (level >= 2 || _g_.m.throwing > 0) {
            print("\nruntime stack:\n");
            traceback(pc, sp, 0, gp);
        }
        if (!didothers && all) {
            didothers = true;
            tracebackothers(gp);
        }
    }
    unlock(_addr_paniclk);

    if (atomic.Xadd(_addr_panicking, -1) != 0) { 
        // Some other m is panicking too.
        // Let it print what it needs to print.
        // Wait forever without chewing up cpu.
        // It will exit when it's done.
        lock(_addr_deadlock);
        lock(_addr_deadlock);
    }
    printDebugLog();

    return docrash;
}

// canpanic returns false if a signal should throw instead of
// panicking.
//
//go:nosplit
private static bool canpanic(ptr<g> _addr_gp) {
    ref g gp = ref _addr_gp.val;
 
    // Note that g is m->gsignal, different from gp.
    // Note also that g->m can change at preemption, so m can go stale
    // if this function ever makes a function call.
    var _g_ = getg();
    var _m_ = _g_.m; 

    // Is it okay for gp to panic instead of crashing the program?
    // Yes, as long as it is running Go code, not runtime code,
    // and not stuck in a system call.
    if (gp == null || gp != _m_.curg) {
        return false;
    }
    if (_m_.locks != 0 || _m_.mallocing != 0 || _m_.throwing != 0 || _m_.preemptoff != "" || _m_.dying != 0) {
        return false;
    }
    var status = readgstatus(gp);
    if (status & ~_Gscan != _Grunning || gp.syscallsp != 0) {
        return false;
    }
    if (GOOS == "windows" && _m_.libcallsp != 0) {
        return false;
    }
    return true;
}

// shouldPushSigpanic reports whether pc should be used as sigpanic's
// return PC (pushing a frame for the call). Otherwise, it should be
// left alone so that LR is used as sigpanic's return PC, effectively
// replacing the top-most frame with sigpanic. This is used by
// preparePanic.
private static bool shouldPushSigpanic(ptr<g> _addr_gp, System.UIntPtr pc, System.UIntPtr lr) {
    ref g gp = ref _addr_gp.val;

    if (pc == 0) { 
        // Probably a call to a nil func. The old LR is more
        // useful in the stack trace. Not pushing the frame
        // will make the trace look like a call to sigpanic
        // instead. (Otherwise the trace will end at sigpanic
        // and we won't get to see who faulted.)
        return false;
    }
    if (gp.m.incgo || findfunc(pc).valid()) { 
        // This wasn't a bad call, so use PC as sigpanic's
        // return PC.
        return true;
    }
    if (findfunc(lr).valid()) { 
        // This was a bad call, but the LR is good, so use the
        // LR as sigpanic's return PC.
        return false;
    }
    return true;
}

// isAbortPC reports whether pc is the program counter at which
// runtime.abort raises a signal.
//
// It is nosplit because it's part of the isgoexception
// implementation.
//
//go:nosplit
private static bool isAbortPC(System.UIntPtr pc) {
    var f = findfunc(pc);
    if (!f.valid()) {
        return false;
    }
    return f.funcID == funcID_abort;
}

} // end runtime_package
